#include "ngx_ngim.h"

static u_char *
ngx_ngim_curl_connection_log_error(ngx_log_t *log, u_char *buf, size_t len);

void
ngx_ngim_init_connection(ngx_connection_t *c)
{
    ngx_uint_t             i;
    ngx_ngim_port_t       *port;
    struct sockaddr       *sa;
    struct sockaddr_in    *sin;
    ngx_ngim_in_addr_t    *addr;
    ngx_ngim_addr_conf_t  *addr_conf;
    ngx_int_t              unix_socket;
#if (NGX_HAVE_INET6)
    struct sockaddr_in6   *sin6;
    ngx_ngim_in6_addr_t   *addr6;
#endif


    /* find the server configuration for the address:port */

    /* AF_INET only */

    port = c->listening->servers;
    unix_socket = 0;

    if (port->naddrs > 1) {

        /*
         * There are several addresses on this port and one of them
         * is the "*:port" wildcard so getsockname() is needed to determine
         * the server address.
         *
         * AcceptEx() already gave this address.
         */

        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
            ngx_ngim_close_connection(c);
            return;
        }

        sa = c->local_sockaddr;

        switch (sa->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            sin6 = (struct sockaddr_in6 *) sa;

            addr6 = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
                    break;
                }
            }

            addr_conf = &addr6[i].conf;

            break;
#endif

        case AF_UNIX:
            unix_socket = 1;

        default: /* AF_INET */
            sin = (struct sockaddr_in *) sa;

            addr = port->addrs;

            /* the last address is "*" */

            for (i = 0; i < port->naddrs - 1; i++) {
                if (addr[i].addr == sin->sin_addr.s_addr) {
                    break;
                }
            }

            addr_conf = &addr[i].conf;

            break;
        }

    } else {
        switch (c->local_sockaddr->sa_family) {

#if (NGX_HAVE_INET6)
        case AF_INET6:
            addr6 = port->addrs;
            addr_conf = &addr6[0].conf;
            break;
#endif

        case AF_UNIX:
            unix_socket = 1;

        default: /* AF_INET */
            addr = port->addrs;
            addr_conf = &addr[0].conf;
            break;
        }
    }

    ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client connected '%V'",
                  c->number, &c->addr_text);

    ngx_ngim_init_session(c, addr_conf);
}


ngx_connection_t *
ngx_ngim_create_connection(int sock , ngx_log_t *log)
{
   socklen_t         socklen = NGX_SOCKADDRLEN;
   //u_char            sa[NGX_SOCKADDRLEN];
   //ngx_int_t         rc;
   ngx_connection_t *c;
   ngx_event_t      *wev,*rev;
   ngx_log_t        *clog;
   //size_t            addr_text_max_len;

   c = ngx_get_connection(sock,log);

   if (c == NULL) {
      ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
                    "ngx_get_connection failed");

      return NULL;
   }

#if (NGX_STAT_STUB)
   (void) ngx_atomic_fetch_add(ngx_stat_active, 1);
#endif

   /*rc  = getpeername(sock,(struct sockaddr*)sa,(socklen_t*)&socklen);
   if(rc != 0){
      ngx_log_error(NGX_LOG_ERR,pc->log,ngx_socket_errno,
                    "cant getpeername sockaddr"); 
      return NULL;
   }*/

   //to-do  fix make sure the size for new c->pool
   c->pool = ngx_create_pool(1024, log);
   if (c->pool == NULL) {
      ngx_ngim_free_connection(c);
      return NULL;
   }

   c->sockaddr = ngx_palloc(c->pool, socklen);
   if (c->sockaddr == NULL) {
      ngx_ngim_free_connection(c);
      return NULL;
   }

   //ngx_memcpy(c->sockaddr, sa, socklen);

   clog = ngx_palloc(c->pool, sizeof(ngx_log_t));
   if (clog == NULL) {
      ngx_ngim_free_connection(c);
      return NULL;
   }

   *clog = *log;
   clog->connection = c->number;
   clog->data       = c;
   clog->handler    = ngx_ngim_curl_connection_log_error;
   clog->action     = "while sub curl connection";

   //c->data = session;
   //session->connection = c; 

   c->recv = ngx_recv;
   c->send = ngx_send;
   c->recv_chain = ngx_recv_chain;
   c->send_chain = ngx_send_chain;

   c->log = clog;
   c->pool->log = clog;

   c->socklen = socklen;
   //c->listening = ls;
   //c->local_sockaddr = ls->sockaddr;

   c->unexpected_eof = 1;

#if (NGX_SOLARIS)
   /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */
   c->sendfile = 0;
#endif

   rev = c->read;
   wev = c->write;

   wev->ready = 1;

   if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT)) {
       /* rtsig, aio, iocp */
       rev->ready = 1;
   }

   rev->log = clog;
   wev->log = clog;

   /*
         * TODO: MT: - ngx_atomic_fetch_add()
         *             or protection by critical section or light mutex
         *
         * TODO: MP: - allocated in a shared memory
         *           - ngx_atomic_fetch_add()
         *             or protection by critical section or light mutex
   */

   c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);

#if (NGX_STAT_STUB)
   (void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
#endif

#if (NGX_THREADS)
    rev->lock = &c->lock;
    wev->lock = &c->lock;
    rev->own_lock = &c->lock;
    wev->own_lock = &c->lock;
#endif

    /*c->addr_text.data = ngx_pnalloc(c->pool,addr_text_max_len);
    if (c->addr_text.data == NULL) {
       //ngx_close_accepted_connection(c);
       return NULL;
    }

    c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->addr_text.data,
                                             addr_text_max_len, 0);
    if (c->addr_text.len == 0) {
       //ngx_close_accepted_connection(c);
       return NULL;
    }*/

    return c;
}

void
ngx_ngim_free_connection(ngx_connection_t *c)
{
    ngx_pool_t   *pool;

    ngx_log_debug0(NGX_LOG_DEBUG_NGIM, c->log, 0, "ngim free close connection");

    ngx_free_connection(c);

    pool  = c->pool;
    if (pool) {
        ngx_destroy_pool(pool);
        c->pool = NULL;
    }

#if (NGX_STAT_STUB)
    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
#endif
}

void ngx_ngim_close_connection(ngx_connection_t *c)
{
    ngx_pool_t                         *pool;

    ngx_log_debug0(NGX_LOG_DEBUG_NGIM, c->log, 0, "ngim close connection");

    pool = c->pool;
    ngx_close_connection(c);

    if(pool){
       ngx_destroy_pool(pool);
       c->pool = NULL;
    }
}


void
ngx_ngim_async_close_connection(ngx_connection_t *c)
{
    ngx_err_t     err;
    ngx_uint_t    log_error, level;
    ngx_socket_t  fd;

    ngx_log_debug(NGX_LOG_DEBUG_NGIM, c->log, 0, 
                  "ngx_ngim_async_close_connection %i",c->fd);

    if (c->fd == -1) {
        ngx_log_error(NGX_LOG_ALERT, c->log, 0, "connection already closed");
        return;
    }

    if (c->read->timer_set) {
        ngx_del_timer(c->read);
    }

    if (c->write->timer_set) {
        ngx_del_timer(c->write);
    }

    if (ngx_del_conn) {
        ngx_del_conn(c, NGX_CLOSE_EVENT);

    } else {
        if (c->read->active || c->read->disabled) {
            ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
        }

        if (c->write->active || c->write->disabled) {
            ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);
        }
    }

#if (NGX_THREADS)

    /*
     * we have to clean the connection information before the closing
     * because another thread may reopen the same file descriptor
     * before we clean the connection
     */

    ngx_mutex_lock(ngx_posted_events_mutex);

    if (c->read->prev) {
        ngx_delete_posted_event(c->read);
    }

    if (c->write->prev) {
        ngx_delete_posted_event(c->write);
    }

    c->read->closed = 1;
    c->write->closed = 1;

    if (c->single_connection) {
        ngx_unlock(&c->lock);
        c->read->locked = 0;
        c->write->locked = 0;
    }

    ngx_mutex_unlock(ngx_posted_events_mutex);

#else

    if (c->read->prev) {
        ngx_delete_posted_event(c->read);
    }

    if (c->write->prev) {
        ngx_delete_posted_event(c->write);
    }

    c->read->closed = 1;
    c->write->closed = 1;

#endif

    //ngx_reusable_connection(c, 0);

    log_error = c->log_error;

    //ngx_free_connection(c);

    fd = c->fd;
    //c->fd = (ngx_socket_t) -1;

    if (ngx_close_socket(fd) == -1) {

        err = ngx_socket_errno;

        if (err == NGX_ECONNRESET || err == NGX_ENOTCONN) {

            switch (log_error) {

            case NGX_ERROR_INFO:
                level = NGX_LOG_INFO;
                break;

            case NGX_ERROR_ERR:
                level = NGX_LOG_ERR;
                break;

            default:
                level = NGX_LOG_CRIT;
            }

        } else {
            level = NGX_LOG_CRIT;
        }

        /* we use ngx_cycle->log because c->log was in c->pool */

        ngx_log_error(level, ngx_cycle->log, err,
                      ngx_close_socket_n " %d failed", fd);
    }
}


static u_char *
ngx_ngim_curl_connection_log_error(ngx_log_t *log, u_char *buf, size_t len)
{
    return ngx_snprintf(buf, len, " while accepting new connection on");
}
